/**************************************************************************** 
* 
* Copyright (c) 2023  C*Core -   All Rights Reserved  
* 
* THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, 
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* 
* PROJECT     : CCFC2011BC           
* DESCRIPTION : CCFC2011BC pwm low level drivers code 
* HISTORY     : Initial version
* @file     pwm_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/

#include "emios_lld.h"
#include "sys.h"
#include "IntcInterrupts.h"
#include "console.h"
#include "siul_lld.h"


/*******************************************************************************
 * @brief      The interrupt is shared with the ICU module, and only one can be 
 *             used at the same time.
 * @param[in]  pitd      pointer to the PWMDriver object
 *             channel     
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void PWM_LLD_Serve_Interrupt(const PWMDriver *pwmdriver, uint8_t channel)
{
    uint32_t sr;

    sr = pwmdriver->emiosp->CH[channel].CSR.R;
    if ((sr & EMIOSS_OVFL) != 0U)
    {
        pwmdriver->emiosp->CH[channel].CSR.R |= EMIOSS_OVFLC;
    }
    if ((sr & EMIOSS_OVR) != 0U)
    {
        pwmdriver->emiosp->CH[channel].CSR.R |= EMIOSS_OVRC;
    }
    if ((sr & EMIOSS_FLAG) != 0U)
    {
        pwmdriver->emiosp->CH[channel].CSR.R |= EMIOSS_FLAGC;
    }
}

/*******************************************************************************
 * @brief      WM low level driver deinitialization
 * @param[in]  PWMDriver * pwmdriver, 
 *             volatile struct EMIOS_tag * emios_base_address 
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
static void PWM_LLD_DeInit(PWMDriver * pwmdriver, volatile struct EMIOS_tag * emios_base_address)
{
    pwmdriver->emiosp = emios_base_address;

    emios_base_address->MCR.R = 0;
    emios_base_address->OUDR.R = 0;
    emios_base_address->UCDIS.R = 0;

    pwmdriver->emiosp = NULL;
    pwmdriver->ch_num.R = 0;
    pwmdriver->sys_clock = 0;
    pwmdriver->emios_prescale = 0;
    pwmdriver->config = NULL;
}


/*******************************************************************************
 * @brief      PWM low level driver deinitialization
 * @param[in]  pwmdriver   PWMD_N
 *             emios_base_address   EMIOS_0 or EMIOS_1
 *             emios_prescale  1-256
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void PWM_LLD_Init(PWMDriver * pwmdriver, volatile struct EMIOS_tag * emios_base_address, uint8_t emios_prescale)
{
    PWM_LLD_DeInit(pwmdriver, emios_base_address);
    /* PWMDriver Init*/
    pwmdriver->emiosp = emios_base_address;
    pwmdriver->ch_num.R = 0;
    pwmdriver->sys_clock = GetSystemClock();
    pwmdriver->emios_prescale = emios_prescale;
    pwmdriver->emiosp->MCR.R = 0;
    pwmdriver->emiosp->MCR.R &= ~EMIOSMCR_MDIS;
    pwmdriver->emiosp->MCR.R &= ~EMIOSMCR_GPREN;
    pwmdriver->emiosp->MCR.R |= EMIOSMCR_GPRE(emios_prescale - 1UL);
    pwmdriver->emiosp->MCR.R |= EMIOSMCR_GPREN;
    pwmdriver->emiosp->MCR.B.GTBE = 1U;
    pwmdriver->emiosp->OUDR.R = 0;
    pwmdriver->emiosp->UCDIS.R = 0xFFFFFFFFU;
}

/*******************************************************************************
 * @brief      PWM_LLD_UC_Channel_DeInit
 * @param[in]  const PWMDriver * pwmdriver, 
 *                   uint8_t channel
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
static void PWM_LLD_UC_Channel_DeInit(const PWMDriver * pwmdriver, uint8_t channel)
{
    /* Channel enables to write registers.*/
    pwmdriver->emiosp->UCDIS.R &= ~(1UL << channel);

    /*DeInit all used registers*/
    pwmdriver->emiosp->CH[channel].CCR.R = 0;
    pwmdriver->emiosp->CH[channel].CADR.R = 0;
    pwmdriver->emiosp->CH[channel].CBDR.R = 0;
    pwmdriver->emiosp->CH[channel].CCNTR.R = 0;
    pwmdriver->emiosp->CH[channel].CSR.R = 0;
}

/*******************************************************************************
 * @brief      PWM_LLD_UC_Channel_Init
 * @param[in]  PWMDriver * pwmdriver, 
 *             uint8_t channel, 
 *             PWMConfig_t * pwmconfig
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void PWM_LLD_UC_Channel_Init(PWMDriver * pwmdriver, uint8_t channel, PWMConfig_t * pwmconfig)
{
    pwmdriver->ch_num.R |=      (1UL << channel);
    pwmdriver->config   =       pwmconfig;

    /* Channel enables to write registers.*/
    pwmdriver->emiosp->UCDIS.R &= ~(1UL << channel);

    /*DeInit all used registers*/
    PWM_LLD_UC_Channel_DeInit(pwmdriver, channel);

    /* Set UC mode.*/
    switch (pwmdriver->config->hw_mode)
    {
        case PWM_OPWFMB_MODE:

            /* Set edge_polarity.*/
            switch (pwmdriver->config->config_opwfmb->edge_polarity)
            {
                case START_FALLING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R &= EMIOSC_EDPOL;
                    break;

                case START_RISING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_EDPOL;
                    break;

                default:
                    break;
            }

            /* Set UC clock prescaler and control register.*/
            pwmdriver->emiosp->CH[channel].CCR.R    &=  ~EMIOSC_UCPREN;

            /* selet clock source.*/
            switch (pwmdriver->config->config_opwfmb->clock_bus)
            {
                case PWM_INTERNAL_COUNTER:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_INTERNAL_COUNTER);
                    break;

                case PWM_COUNTER_BUS_A:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_A);
                    break;

                case PWM_COUNTER_BUS_2:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_2);
                    break;

                default:
                    break;
            }
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPRE(pwmdriver->config->config_opwfmb->uc_prescale);
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPREN;
            /* CBDR = peroid, CADR = dutycycle*/
            pwmdriver->emiosp->CH[channel].CBDR.R = pwmdriver->config->config_opwfmb->peroid_value;
            pwmdriver->emiosp->CH[channel].CADR.R = pwmdriver->config->config_opwfmb->dutycycle_value;
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_OPWFMB;

            break;

        case PWM_MCB_MODE:

            /* Set UC clock prescaler and control register.*/
            pwmdriver->emiosp->CH[channel].CCR.R &= ~EMIOSC_UCPREN;

                        /* selet clock source.*/
            switch (pwmdriver->config->config_mcb->clock_bus)
            {
                case PWM_INTERNAL_COUNTER:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_INTERNAL_COUNTER);
                    break;

                case PWM_COUNTER_BUS_A:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_A);
                    break;

                case PWM_COUNTER_BUS_2:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_2);
                    break;

                default:
                    break;
            }

            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPRE(pwmdriver->config->config_mcb->uc_prescale);
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPREN;

            /* 1<CADR.R<65535*/
            switch (pwmdriver->config->config_mcb->counter_mode)
            {
                case MCB_COUNTER_UP_MODE:
                    pwmdriver->emiosp->CH[channel].CADR.R = pwmdriver->config->config_mcb->up_mode_peroid_value;
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_MCB_UP;
                    break;
                case MCB_COUNTER_UP_DOWN_MODE:
                    pwmdriver->emiosp->CH[channel].CADR.R = (pwmdriver->config->config_mcb->up_down_mode_peroid_value + 2UL) / 2UL;
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_MCB_UP_DOWN;
                    break;
                default:
                    break;
            }
            break;

        case PWM_OPWMB_MODE:

            switch (pwmdriver->config->config_opwmb->edge_polarity)
            {
                case START_FALLING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R &= EMIOSC_EDPOL;
                    break;

                case START_RISING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_EDPOL;
                    break;

                default:
                    break;
            }

            pwmdriver->emiosp->CH[channel].CCR.R    &=  ~EMIOSC_UCPREN;

            /* selet clock source.*/
            switch (pwmdriver->config->config_opwmb->clock_bus)
            {
                case PWM_INTERNAL_COUNTER:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_INTERNAL_COUNTER);
                    break;

                case PWM_COUNTER_BUS_A:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_A);
                    break;

                case PWM_COUNTER_BUS_2:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_2);
                    break;

                default:
                    break;
            }

            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPRE(pwmdriver->config->config_opwmb->uc_prescale);
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPREN;
            pwmdriver->emiosp->CH[channel].CBDR.R = pwmdriver->config->config_opwmb->b;
            pwmdriver->emiosp->CH[channel].CADR.R = pwmdriver->config->config_opwmb->a;
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_OPWMB;
            break;

        case PWM_OPWMCB_MODE:

            /* Set UC clock prescaler and control register.*/
            pwmdriver->emiosp->CH[channel].CCR.R &= ~EMIOSC_UCPREN;
            /* selet clock source.*/
            switch (pwmdriver->config->config_opwmcb->clock_bus)
            {
                case PWM_INTERNAL_COUNTER:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_INTERNAL_COUNTER);
                    break;

                case PWM_COUNTER_BUS_A:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_A);
                    break;

                case PWM_COUNTER_BUS_2:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_BSL(EMIOS_BSL_COUNTER_BUS_2);
                    break;

                default:
                    break;
            }

            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPRE(pwmdriver->config->config_opwmcb->uc_prescale);
            pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_UCPREN;

            switch (pwmdriver->config->config_opwmcb->edge_polarity)
            {
                case START_FALLING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R &= EMIOSC_EDPOL;
                    break;

                case START_RISING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOSC_EDPOL;
                    break;

                default:
                    break;
            }

            /* dead time  =  b*/
            pwmdriver->emiosp->CH[channel].CBDR.R = pwmdriver->config->config_opwmcb->b;
            /* period  =   internal_counter_frequency / (up_down_mode_peroid_value of MCB_MODE)*/
            /* duty cycle   =  (2(A_of_MCB_MODE - a)+2 - b) / (up_down_mode_peroid_value of MCB_MODE)  */
            /* the 2 is modification value according to case MCB_COUNTER_UP_DOWN_MODE in UPPAGE*/
            pwmdriver->emiosp->CH[channel].CADR.R = pwmdriver->config->config_opwmcb->a;

            switch (pwmdriver->config->config_opwmcb->dead_insertion)
            {
                case PWM_LEADING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_OPWMCB_LE;
                    break;

                case PWM_TRAILING_EDGE:
                    pwmdriver->emiosp->CH[channel].CCR.R |= EMIOS_CCR_MODE_OPWMCB_TE;
                    break;
                default:
                    break;
            }
            break;

        default:
            break;
    }

    pwmdriver->emiosp->CH[channel].CCNTR.R = 0;

     /* Channel disables.*/
     pwmdriver->emiosp->UCDIS.R |= (1UL << channel);
}

/*******************************************************************************
 * @brief      PWM_LLD_GPIO_Enable
 * @param[in]  const PWMDriver * pwmdriver, 
 *                   uint8_t channel
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
static void PWM_LLD_GPIO_Enable(const PWMDriver * pwmdriver, uint8_t channel)
{
    if (pwmdriver->emiosp != &EMIOS_1)
    {
        if (channel < 12U)
        {
            SIU.PCR[channel].R = PCR_PA1;         /* assign port pad to Alt Func 1  */
        }
        else
        {
            switch (channel)
            {
                case 12:
                    SIU.PCR[PC12_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 13:
                    SIU.PCR[PC13_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 14:
                    SIU.PCR[PC14_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 15:
                    SIU.PCR[PC15_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 16:
                    SIU.PCR[PE0_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 17:
                    SIU.PCR[PE1_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 18:
                    SIU.PCR[PE2_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 19:
                    SIU.PCR[PE3_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 20:
                    SIU.PCR[PE4_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 21:
                    SIU.PCR[PE5_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 22:
                    SIU.PCR[PE6_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 23:
                    SIU.PCR[PE7_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 24:
                    SIU.PCR[PE11_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 25:
                    SIU.PCR[PG11_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 26:
                    SIU.PCR[PG12_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 27:
                    SIU.PCR[PG13_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                    break;
                case 28:
                    SIU.PCR[PI0_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 29:
                    SIU.PCR[PI1_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 30:
                    SIU.PCR[PI2_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                case 31:
                    SIU.PCR[PI3_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                    break;
                default:
                    break;
            }
        }
    }
    else
    {
        switch (channel)
        {
            case 0:
                SIU.PCR[PG14_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 1:
                SIU.PCR[PG15_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 2:
                SIU.PCR[PH0_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 3:
                SIU.PCR[PH1_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 4:
                SIU.PCR[PH2_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 5:
                SIU.PCR[PH3_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 6:
                SIU.PCR[PH4_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 7:
                SIU.PCR[PH5_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 8:
                SIU.PCR[PH6_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 9:
                SIU.PCR[PH7_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 10:
                SIU.PCR[PH8_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 11:
                SIU.PCR[PG2_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 12:
                SIU.PCR[PG3_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 13:
                SIU.PCR[PG4_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 14:
                SIU.PCR[PG5_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 15:
                SIU.PCR[PG6_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 16:
                SIU.PCR[PG7_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 17:
                SIU.PCR[PG8_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 18:
                SIU.PCR[PG9_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 19:
                SIU.PCR[PE12_PCR].R = PCR_PA2;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 20:
                SIU.PCR[PE13_PCR].R = PCR_PA2;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 21:
                SIU.PCR[PE14_PCR].R = PCR_PA2;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 22:
                SIU.PCR[PE15_PCR].R = PCR_PA2;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 23:
                SIU.PCR[PG0_PCR].R = PCR_PA2;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 24:
                SIU.PCR[PG1_PCR].R = PCR_PA2;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 25:
                SIU.PCR[PF12_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 26:
                SIU.PCR[PF13_PCR].R = PCR_PA1;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 27:
                SIU.PCR[PF14_PCR].R = PCR_PA2;    /* assign port pad to Alt Func 1  and input*/
                break;
            case 28:
                SIU.PCR[PI4_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 29:
                SIU.PCR[PI5_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 30:
                SIU.PCR[PI6_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            case 31:
                SIU.PCR[PI7_PCR].R = PCR_PA1;     /* assign port pad to Alt Func 1  and input*/
                break;
            default:
                break;
        }
    }
}


/*******************************************************************************
 * @brief      PWM_LLD_UC_Channel_Enable
 * @param[in]  const PWMDriver * pwmdriver, 
 *                   uint8_t channel
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void PWM_LLD_UC_Channel_Enable(const PWMDriver * pwmdriver, uint8_t channel)
{
    PWM_LLD_GPIO_Enable(pwmdriver, channel);

    /* Channel enables.*/
    pwmdriver->emiosp->UCDIS.R &= ~(1UL << channel);
}

/*******************************************************************************
 * @brief      PWM_LLD_UC_Channel_Disable
 * @param[in]  const PWMDriver * pwmdriver, u
 *                   int8_t channel
 * @param[out] None
 * @retval     None
 * @notapi 
 *******************************************************************************/
void PWM_LLD_UC_Channel_Disable(const PWMDriver * pwmdriver, uint8_t channel)
{
    /* Channel disable.*/
    pwmdriver->emiosp->UCDIS.R |= (1UL << channel);
}
